use 5.010;
use strict;
use warnings;

package JasPerl::JSTL::Core::LoopTagSupport;
use parent qw(JasPerl::TagExt::JspBodyTag);

use JasPerl::Exceptions;
use JasPerl::PageContext qw(:scopes);
use JasPerl::JSTL::Core::LoopTagStatus;

sub get_current {
    return $_[0]->{current};
}

sub get_loop_status {
    my $self = shift;
    # caching status creates a circular reference
    $self->{status} = JasPerl::JSTL::Core::LoopTagStatus->new($self)
        unless exists $self->{status};
    return $self->{status};
}

sub set_var {
    $_[0]->{item_id} = $_[1];
}

sub set_var_status {
    $_[0]->{status_id} = $_[1];
}

sub prepare {
    # default implementation does nothing
}

sub release {
    my $self = shift;

    # remove nested variables
    my $context = $self->get_jsp_context();
    $context->remove_attribute($self->{item_id}, PAGE_SCOPE)
        if defined $self->{item_id};
    $context->remove_attribute($self->{status_id}, PAGE_SCOPE)
        if defined $self->{status_id};

    # remove circular status reference
    delete $self->{status};
}

sub validate_begin {
    JasPerl::JspTagException->throw("attribute 'begin' < 0")
        if defined $_[0]->{begin} and $_[0]->{begin} < 0;
}

sub validate_step {
    JasPerl::JspTagException->throw("attribute 'step' <= 0")
        if defined $_[0]->{step} and $_[0]->{step} <= 0;
}

sub validate_end {
    JasPerl::JspTagException->throw("attribute 'end' < 0")
        if defined $_[0]->{end} and $_[0]->{end} < 0;
}

sub do_tag {
    my $self = shift;
    my $begin = $self->{begin} // 0;
    my $end = $self->{end} // -1;
    return unless $end == -1 or $begin <= $end;

    $self->prepare();

    eval {
        my $context = $self->get_jsp_context();

        for ($self->{index} = $begin; $begin >= 0; $begin--) {
            return unless $self->has_next();
            $self->{current} = $self->next();
        }

        $context->set_attribute($self->{status_id}, $self->get_loop_status())
            if defined $self->{status_id};

        for ($self->{count} = 1; !$self->{last}; $self->{count}++) {
            $context->set_attribute($self->{item_id}, $self->{current})
                if defined $self->{item_id};

            for (my $i = $self->{step} // 1; $i; $i--) {
                $self->{last} = ($end != -1 && $self->{index} + $i >= $end)
                    and last;
                $self->{last} = (!$self->has_next())
                    and last;
                $self->next() unless $i == 1;
            }

            $self->do_body();

            last if $self->{last};
            $self->{current} = $self->next();
            $self->{index} += $self->{step} || 1;
        }
    };

    my $e = $@;
    $self->release();
    die $e if $e;
}

sub has_next {
    die "not implemented";
}

sub next {
    die "not implemented";
}

1;
